﻿using System;
using System.IO;
using System.Collections;

public class B3Helper {

	static void _Write(Stream output, int v)
	{
		output.WriteByte((byte)v);
	}

	static void _Write(Stream output, B3Types tag)
	{
		int v = (int)tag;
		_Write(output, v);
	}


	static int _Read(Stream input) 
	{
		return input.ReadByte ();
	}

	static B3Types _ReadTag(Stream input) {
		return (B3Types) _Read (input);
	}

	public static void WriteInt (Stream output, int v)
	{
		switch (v) {
		case -1:
			_Write(output, B3Types.INT_N1);
			break;
		case 0:
			_Write(output, B3Types.INT_0);
			break;
		case 1:
			_Write(output, B3Types.INT_1);
			break;
		case 2:
			_Write(output, B3Types.INT_2);
			break;
		case 3:
			_Write(output, B3Types.INT_3);
			break;
		case 4:
			_Write(output, B3Types.INT_4);
			break;
		case 5:
			_Write(output, B3Types.INT_5);
			break;
		case 6:
			_Write(output, B3Types.INT_6);
			break;
		case 7:
			_Write(output, B3Types.INT_7);
			break;
		case 8:
			_Write(output, B3Types.INT_8);
			break;
		case 9:
			_Write(output, B3Types.INT_9);
			break;
		case 10:
			_Write(output, B3Types.INT_10);
			break;
		case 11:
			_Write(output, B3Types.INT_11);
			break;
		case 12:
			_Write(output, B3Types.INT_12);
			break;
		case 13:
			_Write(output, B3Types.INT_13);
			break;
		case 14:
			_Write(output, B3Types.INT_14);
			break;
		case 15:
			_Write(output, B3Types.INT_15);
			break;
		case 16:
			_Write(output, B3Types.INT_16);
			break;
		case 17:
			_Write(output, B3Types.INT_17);
			break;
		case 18:
			_Write(output, B3Types.INT_18);
			break;
		case 19:
			_Write(output, B3Types.INT_19);
			break;
		case 20:
			_Write(output, B3Types.INT_20);
			break;
		case 21:
			_Write(output, B3Types.INT_21);
			break;
		case 22:
			_Write(output, B3Types.INT_22);
			break;
		case 23:
			_Write(output, B3Types.INT_23);
			break;
		case 24:
			_Write(output, B3Types.INT_24);
			break;
		case 25:
			_Write(output, B3Types.INT_25);
			break;
		case 26:
			_Write(output, B3Types.INT_26);
			break;
		case 27:
			_Write(output, B3Types.INT_27);
			break;
		case 28:
			_Write(output, B3Types.INT_28);
			break;
		case 29:
			_Write(output, B3Types.INT_29);
			break;
		case 30:
			_Write(output, B3Types.INT_30);
			break;
		case 31:
			_Write(output, B3Types.INT_31);
			break;
		case 32:
			_Write(output, B3Types.INT_32);
			break;
		default:
			if (B3Constants.b8(v)) {
				_Write(output, B3Types.INT_8B);
				_Write(output, v);
			} else if (B3Constants.b16(v)) {
				_Write(output, B3Types.INT_16B);
				_Write(output, (byte) ((v >> 8) & 0xff));
				_Write(output, (byte) ((v >> 0) & 0xff));
			} else {
				_Write(output, B3Types.INT_32B);
				_Write(output, (byte) ((v >> 24) & 0xff));
				_Write(output, (byte) ((v >> 16) & 0xff));
				_Write(output, (byte) ((v >> 8) & 0xff));
				_Write(output, (byte) ((v >> 0) & 0xff));
			}
			break;
		}
	}

	public static int ReadInt (Stream input)
	{
		B3Types tag = _ReadTag(input);
		return ReadInt(input, tag);
	}
	
	public static int ReadInt(Stream input, B3Types tag)  {
		switch (tag) {
		case B3Types.INT_N1:
			return -1;
		case B3Types.INT_0:
			return 0;
		case B3Types.INT_1:
			return 1;
		case B3Types.INT_2:
			return 2;
		case B3Types.INT_3:
			return 3;
		case B3Types.INT_4:
			return 4;
		case B3Types.INT_5:
			return 5;
		case B3Types.INT_6:
			return 6;
		case B3Types.INT_7:
			return 7;
		case B3Types.INT_8:
			return 8;
		case B3Types.INT_9:
			return 9;
		case B3Types.INT_10:
			return 10;
		case B3Types.INT_11:
			return 11;
		case B3Types.INT_12:
			return 12;
		case B3Types.INT_13:
			return 13;
		case B3Types.INT_14:
			return 14;
		case B3Types.INT_15:
			return 15;
		case B3Types.INT_16:
			return 16;
		case B3Types.INT_17:
			return 17;
		case B3Types.INT_18:
			return 18;
		case B3Types.INT_19:
			return 19;
		case B3Types.INT_20:
			return 20;
		case B3Types.INT_21:
			return 21;
		case B3Types.INT_22:
			return 22;
		case B3Types.INT_23:
			return 23;
		case B3Types.INT_24:
			return 24;
		case B3Types.INT_25:
			return 25;
		case B3Types.INT_26:
			return 26;
		case B3Types.INT_27:
			return 27;
		case B3Types.INT_28:
			return 28;
		case B3Types.INT_29:
			return 29;
		case B3Types.INT_30:
			return 30;
		case B3Types.INT_31:
			return 31;
		case B3Types.INT_32:
			return 32;
		case B3Types.INT_8B: {
			int v = (sbyte)_Read(input);
			return v;
		}
		case B3Types.INT_16B: {
			int value1 = _Read(input);
			int value2 = _Read(input);
			
			int v = (((value1 & 0xff) << 8) + ((value2 & 0xff) << 0));
			return v;
		}
		case B3Types.INT_32B: {
			int value1 = _Read(input);
			int value2 = _Read(input);
			int value3 = _Read(input);
			int value4 = _Read(input);
			
			int v = ((value1 & 0xff) << 24) + ((value2 & 0xff) << 16)
				+ ((value3 & 0xff) << 8) + ((value4 & 0xff) << 0);
			return v;
		}
		default:
			throw new IOException("unknow type.");
		}
	}

	public static byte[] ToBytes(Hashtable map)
	{
		MemoryStream stream = new MemoryStream();
		ToBytes(map, stream);
		stream.Flush();
		byte[] r2 = stream.ToArray();
		stream.Close();
		stream.Dispose();
		return r2;
	}

	public static void ToBytes(Hashtable map, string path)
	{
		FileStream fs = File.OpenWrite(path);
		ToBytes(map, fs);
		fs.Flush();
		fs.Close();
		fs.Dispose();
	}

	public static void ToBytes(Hashtable map, Stream stream)
	{
		B3OutputStream output = new B3OutputStream(stream);
		output.WriteMap(map);
	}


	public static byte[] ToBytes(ArrayList list)
	{
		MemoryStream stream = new MemoryStream();
		ToBytes(list, stream);
		stream.Flush();
		byte[] r2 = stream.ToArray();
		stream.Close();
		stream.Dispose();
		return r2;
	}

	public static void ToBytes(ArrayList list, string path)
	{
		FileStream fs = File.OpenWrite(path);
		ToBytes(list, fs);
		fs.Flush();
		fs.Close();
		fs.Dispose();
	}

	public static void ToBytes(ArrayList list, Stream stream)
	{
		B3OutputStream output = new B3OutputStream(stream);
		output.WriteList(list);
	}

	public static Hashtable ParseMap(byte[] b)
	{
		MemoryStream stream = new MemoryStream(b);
		Hashtable map = ParseMap(stream);
		stream.Close();
		stream.Dispose();
		return map;
	}

	public static Hashtable ParseMap(string path)
	{
		FileStream stream = File.OpenRead(path);
		Hashtable map = ParseMap(stream);
		stream.Close();
		stream.Dispose();
		return map;
	}

	public static Hashtable ParseMap(Stream stream)
	{
		B3InputStream input = new B3InputStream(stream);
		return input.ReadMap();
	}

	public static ArrayList ParseList(byte[] b)
	{
		MemoryStream stream = new MemoryStream(b);
		ArrayList list = ParseList(stream);
		stream.Close();
		stream.Dispose();
		return list;
	}
	
	public static ArrayList ParseList(string path)
	{
		FileStream stream = File.OpenRead(path);
		ArrayList list = ParseList(stream);
		stream.Close();
		stream.Dispose();
		return list;
	}
	
	public static ArrayList ParseList(Stream stream)
	{
		B3InputStream input = new B3InputStream(stream);
		return input.ReadList();
	}
}
